#!/bin/bash

. $LKP_SRC/lib/result.sh
. $LKP_SRC/lib/lkp_cmd.sh
. $LKP_SRC/lib/env.sh
. $LKP_SRC/lib/constant.sh
. $LKP_SRC/lib/lkp_path.sh
. $LKP_SRC/lib/host.sh

if ! [ $(id -u) = 0 ]; then
	echo "This script must be run as root" 1>&2
	exit 1
fi

script_name=$(basename $0)

[ -n "$HOSTNAME" ] || HOSTNAME=$(hostname)		#?chomp
[ -n "$LKP_SRC" ] || export LKP_SRC=$(dirname $(dirname $(readlink -e -v $0)))
TMP="$LKP_SRC/tmp"

usage() {
	echo "Usage: lkp $script_name [options] [<script>/<jobfile>]"
	echo "options: "
	echo "-f|--force: force to install testsuites"
	echo "--hdd partition: HDD partition for IO tests"
	echo "--ssd partition: SSD partition for IO tests"
	echo "--dry-run: preview changes will made testbox by install"
	echo "--china: install gem packages from the mirror in China"
	echo "--skip-base: skip to install base support"
	echo "--help: show this message"
}

hdd_index=1
ssd_index=1

while [ $# -gt 0 ]
do
	case "$1" in
		--help)
		usage
		exit
		;;
		--hdd)
		hdd_partitions[$hdd_index]=$2
		hdd_index=$((hdd_index+1))
		shift
		;;
		--ssd)
		ssd_partitions[$ssd_index]=$2
		ssd_index=$((ssd_index+1))
		shift
		;;
		--dry-run)
		DRY_RUN=0
		;;
		--china)
		CHINA=0
		;;
		--force|-f)
		FORCE_MODE=true
		;;
		--skip-base)
		SKIP_BASE=true
		;;
		*)
		break
		;;
	esac
	shift
done

validate_parameters()
{
    local ret=0
    for filename in "$@"
    do
        if [ -x "$filename" ]; then
            continue
        elif [ ${filename##*.} = "yaml" ]; then
            if [ -f $filename ]; then
                grep -q '^arch: ' $filename || {
                    echo "only atomic jobs can be installed" >&2
                    echo "Please run lkp split-job $filename first" >&2
                    ret=1
                }
            else
                echo "$0: cannot find file $filename" >&2
                ret=1
            fi
        else
            echo "$0: skip unknown parameter $filename" >&2
            ret=1
        fi
    done
    return $ret
}

make_wakeup() {
	echo "make -C $LKP_SRC/bin/event"
	[ -n "$DRY_RUN" ] && return

	[ -x "$LKP_SRC/bin/event/wakeup" ] || {
		make -C "$LKP_SRC/bin/event" wakeup
	}
}

# todo: make paths configurable and less intrusive to developer's system
# currently it's hard coded in a number of places, should be changed together
create_lkp_dirs() {
	[ -n "$DRY_RUN" ] && return

	mkdir -p $TMP
	mkdir -p $KTEST_PATHS_DIR
	mkdir -p '/lkp/benchmarks'
}

bundle_install_china() {
	local tmpdir=$(mktemp -d /tmp/lkp-install-china-XXXXXX)
	trap "rm -rf $tmpdir" EXIT
	cp $LKP_SRC/Gemfile $tmpdir/Gemfile
	cp $LKP_SRC/Gemfile.lock $tmpdir/Gemfile.lock
	(cd $tmpdir || exit; bundle config --local mirror.https://rubygems.org https://gems.ruby-china.com; bundle install)
}

makepkg_install() {
	local pkg=$1
	local force_opt=

	[ -n "$FORCE_MODE" ] && force_opt="-f"

	local pkg_dir=$(get_pkg_dir $pkg)
	if [ -n "$pkg_dir" ]; then
		(
			cd "$pkg_dir" && \
			PACMAN="$LKP_SRC/sbin/pacman-LKP" CARCH=$arch BUILDDIR="$LKP_SRC/tmp-pkg" \
			"$LKP_SRC/sbin/makepkg" --config "$(lkp_src)/etc/makepkg.conf" -s -i --skippgpcheck "$force_opt"
		)
	else
		return 0
	fi
}

verify_install() {
	local pkg=$1
	local pkg_lkp=$pkg-lkp

	# FIXME consider to put below mapping to adaptation
	#
	# These are not real packages in debian but are required commands, which are
	# installed by the corresponding os packages. Need use the name of the os
	# packages so that dpkg -V can do the right check. The other way is to use
	# has_cmd to check installed or not, but "which perf" returns /usr/bin/perf that
	# is a wrapper instead of actual perf.
	#
	# Here also use -f /.dockerenv as a hack to only do this inside docker to reduce
	# unnecessary re-installation that requires clone the linux repo.
	[ -f /.dockerenv -a $DISTRO = debian ] && {
		[ "$pkg" = perf ] && pkg=linux-perf
	}

	case $DISTRO in
		debian|ubuntu)
			[[ ! $(dpkg -V "$pkg" 2>&1) ]] || [[ ! $(dpkg -V "${pkg_lkp}" 2>&1) ]];;
		centos|fedora|amazon_linux)
			[[ ! $(rpm -V "$pkg" 2>&1) ]] || [[ ! $(rpm -V "${pkg_lkp}" 2>&1) ]];;
		*)
			return 1;;
	esac
}

makepkg_install_packages() {
	local script=$1
	local distro=$2

	local packages dev_packages pkg
	packages="$(get_dependency_packages ${distro} ${script} pkg)"
	dev_packages="$(get_dependency_packages ${distro} ${script}-dev pkg)"
	packages="$(echo $packages $dev_packages | tr '\n' ' ')"
	[ -n "$packages" ] && [ "$packages" != " " ] || return

	for pkg in $packages; do
		[ "$pkg" = "$script" ] && continue
		if ! verify_install $pkg; then
			install_packages "$pkg" "$distro"
			makepkg_install_packages "$pkg" "$distro"
			makepkg_install "$pkg" || echo "Install $pkg failed"
		fi
	done
}

makepkg_install_benchmark() {
	local pkg=$1
	if grep -w "^$pkg:" "$LKP_SRC/distro/adaptation-pkg/$distro"; then
		makepkg_install "$pkg" || echo "Install $pkg failed"
	fi
}

install_packages() {
	local script=$1
	local distro=$2

	local packages="$(get_dependency_packages ${distro} ${script})"
	local dev_packages="$(get_dependency_packages ${distro} ${script}-dev)"
	local generic_packages="$(echo $packages $dev_packages | tr '\n' ' ')"
	[ -n "$generic_packages" -a "$generic_packages" != " " ] || return

	[ "$distro" = "debian" ] && remove_packages_repository
	echo "Use: $LKP_SRC/distro/installer/$distro install $generic_packages"
	[ -n "$DRY_RUN" ] && return

	if [ "$distro" = "centos" -o "$distro" = "aliyun" ]; then
		local ocfs2_tools_name=$(echo "$generic_packages" | grep 'ocfs2-tools')
		if [ -n "$ocfs2_tools_name" ]; then
			. $LKP_SRC/distro/$distro
			install_ocfs2_tools "$ocfs2_tools_name"
		fi
	fi

	$LKP_SRC/distro/installer/$distro $generic_packages || {
		echo "Cannot install some packages of ${script} depends"
		exit 1
	}
}

build_install_benchmarks() {
	local script=$1
	local distro=$2
	local force_install=$3

	[ -x "$LKP_SRC/pack/$script" ] || return 0

	echo "Making $script benchmark for $distro"
	[ -n "$DRY_RUN" ] && return

	[ "$script" = 'perf' ] && perf -v 2>/dev/null && {
		echo "perf tool exists, skip building"
		return 0
	}

	local pkg_name
	pkg_name=$(printf '%s-LKP\n' "$script" | sed 's/_/-/g')

	$LKP_SRC/sbin/pack -d $distro -c $force_install $script
	if [ $? -eq 0 ]; then
		echo "Installing $pkg_name"

		case $distro in
		debian|ubuntu)
			local deb_pkg=/tmp/$pkg_name.deb
			[ -f $deb_pkg ] || return 0
			dpkg -i $deb_pkg 2>/tmp/dpkg_error || {
				grep -v "dpkg: warning: files list file for package '.*' missing;" /tmp/dpkg_error
				return 1
			}
			;;
		fedora|amazon_linux)
			BUILD_DIR=$(get_build_dir $script)
			local rpm_pkg=$BUILD_DIR/$pkg_name/RPMS/$pkg_name.$arch.rpm
			[ -f $rpm_pkg ] || return
			rpm -ivh --replacepkgs $rpm_pkg ;;
		*)
			echo "Just make /lkp/benchmarks/$script-$arch.cgz" ;;
		esac
	else
		echo "Making $pkg_name failed"
		return 1
	fi
}

install_ruby()
{
	if [ "$(gem --version)" != "3.2.3" ]; then
		curl -sSL https://rvm.io/mpapis.asc | gpg2 --import -
		curl -sSL https://rvm.io/pkuczynski.asc | gpg2 --import -
		curl -sSL https://get.rvm.io | bash -s stable
		source /etc/profile.d/rvm.sh
		rvm install ruby-2.6.10
		gem update --system 3.2.3
	fi
}

install_fakeroot()
{
	if ! [ -x /usr/bin/fakeroot ]; then
		(
			git clone https://github.com/mackyle/fakeroot
			cd fakeroot || exit
			libtoolize && aclocal && autoconf && autoheader && automake --add-missing || exit
			./configure --prefix /usr && make && make install || exit
                        cd .. && rm -rf fakeroot || exit
		)
	fi
}

is_ruby_version_ge()
{
	local other_version=$1
	# ruby 2.0.0p648 (2015-12-16) [x86_64-linux]
	local ruby_version=$(ruby --version | grep -oE "[0-9]+\.[0-9]+\.[0-9]+")

	local higher_version=$(echo -e "$other_version\n$ruby_version" | sort -V | tail -n1)

	[ "$ruby_version" = "$higher_version" ]
}

install_base_support()
{
	install_packages "lkp" "$distro"

	if [ "$distro" = "amazon_linux" -a "$_system_version" = "2" ]; then
		install_ruby
		install_fakeroot
	fi

	# https://bundler.io/compatibility.html
	# Bundler compatibility with Ruby & RubyGems
	# The latest Bundler release should always support, at the very least, all rubies that have not yet reached their End of Life date.
	# The latest Bundler release should always support all RubyGems versions higher than or equal to the one shipped with the oldest Ruby version supported. RubyGems is not allowed to be downgraded further than that, so upgrading or downgrading RubyGems should not affect Bundler.
	# That effectively comes down to the following:
	# Bundler 2.4 or higher
	# 	requires a minimum ruby version of 2.6.0, and a minimum RubyGems version of 3.0.1 (version of RubyGems shipped with ruby 2.6.0).
	# Bundler 2.0 through 2.3
	# 	requires a minimum ruby version of 2.3.0, and a minimum RubyGems version of 2.5 (version of RubyGems shipped with ruby 2.3.0).
	# Bundler 1
	# 	Requires a minimum Ruby version of 1.8.7, and a minimum RubyGems version of 1.3.6 (version of RubyGems shipped with ruby 1.8.7).

	local bundler_version
	if is_ruby_version_ge "3.2.0"; then
		# bundler-1.17.3/lib/bundler/shared_helpers.rb:35: warning: Pathname#untaint is deprecated and will be removed in Ruby 3.2.
		bundler_version="2.3.26"
	else
		# install bundler 1 to support ruby 2.0.0 at minimum
		bundler_version="1.17.3"
	fi

	if [ -n "$CHINA" ]; then
		gem install bundler -v $bundler_version --clear-sources -s https://gems.ruby-china.com/
		bundle_install_china
	else
		(
			cd $LKP_SRC || exit
			gem install bundler -v $bundler_version
			bundle install
		)
	fi

	make_wakeup
	create_lkp_user
	create_lkp_dirs
	create_host_config
}

enable_centos_source(){
    if ! yum list installed | grep -q yum-utils; then
        yum install -y yum-utils
    fi

    if [ "$_system_version" = 9 ]; then
	echo "enable source crb"
        dnf config-manager --set-enabled crb
    elif [ "$_system_version" = 8 ]; then
	echo "enable source powertools"
        dnf config-manager --set-enabled powertools
    fi

}

. $LKP_SRC/lib/install.sh

detect_system
distro=$_system_name_lowercase

echo "distro=$distro"
echo "version=$_system_version"

[ "$distro" = "centos" ] && enable_centos_source

DISTRO=$distro
export DISTRO

[ -x $LKP_SRC/distro/installer/$distro ] || {
	echo "Not a supported system, cannot install packages."
	exit 1
}

export LKP_LOCAL_RUN=1

validate_parameters $@ || exit 1

[ -n "$SKIP_BASE" ] || install_base_support

for filename in "$@"
do
	scripts=
	if [ -x "$filename" ]; then
		scripts=$(basename $filename)
	elif [ ${filename##*.} = "yaml" ]; then
		parse_yaml $filename
	fi

	for script in $scripts
	do
		[ -L "$LKP_SRC/pack/$script" ] && {
			real_script=$(readlink "$LKP_SRC/pack/$script")
			script=$(basename "$real_script")
		}

		pkg_dir=$(get_pkg_dir $script)
		[ -z "$makepkg_once" ] && [ -n "$pkg_dir" ] && ! [ -x "$LKP_SRC/pack/$script" ] && {
			install_packages "makepkg" $distro
			makepkg_once=1
		}

		install_packages "$script" $distro
		makepkg_install_packages "$script" $distro

		benchmark_package=$(get_adaptation_pkg $distro "$script")
		if [ -n "$FORCE_MODE" ] || ! verify_install "$benchmark_package"; then
			makepkg_install_benchmark "$script"
		fi

		[ -n "$FORCE_MODE" ] && install_opt="-f"
		build_install_benchmarks "$script" $distro $install_opt || exit 1
	done
done
